关于原生js的一些研究

calleecaller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function inner(){

console.log(arguments.callee);//指向拥有这个arguments对象的函数,即inner()

console.log(arguments.callee.caller);//这个属性保存着调用当前函数的函数的引用,即outer()

console.log(inner.caller);//[Function: outer]

}

function outer(){

inner();

}

outer();

callee放回正在执行的函数本身的引用,它是arguments的一个属性

caller 返回一个函数的引用,这个函数调用了当前的函数。

严格模式下,不允许访问arguments.calleearguments.caller属性,主要体现在arguments.[[Get]]内部方法

严格模式下,arguments,arguments.callee,arguments.caller,arguments.callee.caller也不允许再被赋值。如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'use strict';

// 两次都是1

void function fn(a) {

console.log(arguments[0]);

a = 2;

console.log(arguments[0]);

}(1);

//function(){}(); 会运行错误, 如下可以正确被运行.

void function(){

console.log('hi');//hi

}();


在使用立即执行的函数表达式时,可以利用 void 运算符让 JavaScript 引擎把一个函数识别成函数表达式而不是函数声明(语句)。

实参和形参

1
2
3
4
5
6
7
8
9
var add = function (a,b) {

console.log(arguments.length);//3,表示实参长度

console.log(arguments.callee.length);//2,表示形参长度

};

add(1,2,3);

Array.prototype.slice.call(arguments)

slice有两个用法,一个是String.slice,一个是Array.slice,第一个返回的是字符串,第二个返回的是数组。

Array.prototype.slice.call(arguments)能够将arguments转成数组,那么就是arguments.toArray().slice();

因为arguments并不是真正的数组对象,只是与数组类似而已,所以它并没有slice这个方法,而Array.prototype.slice.call(arguments)可以理解成是将arguments转换成一个数组对象,让arguments具有slice()方法。 比如:

1
2
3
var arr = [1,2,3,4];

console.log(Array.prototype.slice.call(arr,2));//[3,4]

同样,还有Array.prototype.forEach.call()forEach() 方法让数组的每一项都执行一次给定的函数。

String()

我们可以用String()来确定某一变量是否是null或者undefined

1
2
3
4
5
6

var a , b = null;

String(a);//undefined

String(b);//null

直接调用String()作为方法时,将会执行类型转换,返回经过toString(value)得到的字符串字面量(与new String()不同),或者空字符串(’’).

window 对象

1
2
3
4
5
6
7
8
9
10
11
<script>

(function () {

var root = this;

console.log(root);//即window对象

})();

</script>

打开控制台,你可以看到window对象的一系列属性和方法:

new function

1
2
3
4
5
6
7
8
9
10
11
12
13
14

var a = function () {};

console.log(typeof a);//function


var b = new function () {};

console.log(typeof b);//object


var c = new Function ();

console.log(typeof c);//function

new function 是一个JavaScript中用户自定义的对象

1
2
3
4
5
6
7
8
9
var obj = function (name) {

this.name = name;

};

var b = new obj('trigkit4');

console.log(b.name);

js中的false和true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

//false

console.log(Boolean(''));//false

console.log(Boolean(null));//false

console.log(Boolean(undefined));//false

console.log(Boolean(0));//false

console.log(Boolean(false));//false

console.log(Boolean(NaN));//false


//true

console.log(Boolean(' '));//true

console.log(Boolean('NaN'));//true

除了false,null,undefined,空字符串'',数字0和NaN以外,其他所有值都被当做是真,包括true,字符串""里包含的值,以及所有对象。

valueOf()toString()

valueOf()toString()方法是所有ECMAScript对象拥有的内置方法。操作对象时,valueOf()toString()会被隐式的调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

//valueOf()

console.log(Object.valueOf());//[Function: Object]

console.log(Object.prototype.valueOf());//{}

var boo = new Boolean(1);

console.log(boo.valueOf());//true


var bar = Boolean(0);
console.log(bar.valueOf());//false


var str = String("trigkit4");
console.log(str.valueOf());//trigkit4
console.log(null.valueOf());//TypeError
console.log(undefined.valueOf());//TypeError



//toString()

console.log(Object.prototype.toString());//[object Object]

console.log(Object.toString());//function Object() { [native code] }

Object.prototype.toString.call(null);//[object Null]

Object.prototype.toString.call(undefined);//[object Undefined]

{a: 'b'}.toString();//[object Object]

valueOf()方法的目的是将对象转换成最有意义的原始值([[PrimitiveValue]])。即ECMAScript的5种基本类型中的三种,布尔值、数字、字符串

valueOf方法被调用时,会调用内置的ToObject,并将this作为参数传进去。ToObject检测会根据参数类型进行数值的转换:

1
2
3
4
5
6
7
8
9
10
11
Undefined - 抛出TypeError异常

Null - 抛出TypeError异常

Boolean - 创建一个Boolean对象,调用ToBoolean生成[[PrimitiveValue]]

Number - 创建一个Number对象,调用ToNumber生成[[PrimitiveValue]]

String - 创建一个String对象,调用ToString生成[[PrimitiveValue]]

Object - 对象本身

ECMAScript对象的大多数操作的转换结果是字符串,这两个方法的结果是相同的。但是如果操作的对象为NumberBoolean或者Date,结果就不同了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var foo = {

toString: function () {

return "foo";

},

valueOf: function () {

return 5;

}

};



console.log(foo + "bar"); // 5bar

console.log([foo, "bar"].join("")); // foobar

在这个上下文环境中,我们使用"+"操作符来使字符串连接,但是,foo并没有使用toString来转换成字符串,它使用valueOf转换成一个number,这并不是我们想要的,

但它是如何工作的,这是+运算符的算术和字符串连接超载的副作用。"+"操作符有一个明确的处理过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

1.评估左手侧,并得到该值。

2.评估右手侧,并获得该值。

3.同时在左手和右手侧调用ToPrimitive(无提示)

4.如果任何原始值是一个字符串,然后跳到7

5.在这两个值调用ToNumber。

6.返回值的总和。

7.在这两个值调用toString。

8.返回的值连接起来

setInterval和setTimeout

1
2
3
4
5
6
7
8
9
10
11
12

alert(1);

setTimeout("alert(2)", 0);

alert(3);

执行顺序为:132,虽然延时了0ms

setTimeout 0 //正常情况下javascript都是按照顺序执行的。但是我们可能让该语句

后面的语句执行完再执行本身,这时就可以用到setTimeout延时0ms来实现了。

cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。

1
2
3
4

document.cookie = “user = 值;expires = 过期时间;path = 路径访问;

domain = 域名访问;secure = 安全的https限制通信"

设置cookie我们一般都封装成一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

function addCookie(sName,sValue,day) {

var expireDate = new Date();

expireDate.setDate(expireDate.getDate()+day);;

//设置失效时间

document.cookie = escape(sName) + '=' + escape(sValue) +';

expires=' + expireDate.toGMTString();6 //escape()汉字转成unicode编码,toGMTString() 把日期对象转成字符串


}

删除cookie

为了删除一个cookie,可以将其过期时间设定为一个过去的时间,例如:

1
2
3
4
5
6
7
8
9
10
11
12

//获取当前时间

var date=new Date();

//将date设置为过去的时间

date.setTime(date.getTime()-10000);

//将userId这个cookie删除

document.cookie="userId=828; expires="+date.toGMTString();

给cookie设置终止日期

到现在为止,所有的cookie都是单会话cookie,即浏览器关闭后这些cookie将会丢失,事实上这些cookie仅仅是存储在内存中,而没有建立相应的硬盘文件。

在实际开发中,cookie常常需要长期保存,例如保存用户登录的状态。这可以用下面的选项来实现:

1
document.cookie="userId=828; expiress=GMT_String";

其中GMT_String是以GMT格式表示的时间字符串,这条语句就是将userId这个cookie设置为GMT_String表示的过期时间,超过这个时间,cookie将消失,不可访问。例如:如果要将cookie设置为10天后过期,可以这样实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script language="JavaScript" type="text/javascript">

//获取当前时间

var date=new Date();

var expiresDays=10;

//将date设置为10天以后的时间

date.setTime(date.getTime()+expiresDays*24*3600*1000);

//将userId和userName两个cookie设置为10天后过期

document.cookie="userId=828; userName=hulk; expires="+date.toGMTString();

</script>

对象和函数可以如数组一样,用属性名或方法名作为下标来访问:

对象的创建

1
2
3
4
5
6
7
8

//对象的创建

function MyFunc(){}

var obj1 = new MyFunc();//使用new操作符,借助MyFun函数,就创建了一个对象

var obj2 = new MyFunc;//函数也可以没有括号,但仍将调用该函数

可以把上面的代码改写成这种等价形式:

1
2
3
4
5
6

function MyFunc(){};

var obj1 = {};//创建一个对象

MyFunc.call(obj1);//将obj1对象作为this指针调用MyFunc函数

作用域

通过自执行的匿名函数你可以把所有原本属于全局的变量都隐藏起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

//创建一个新的匿名函数,作为包装

(function () {

//变量原本应该是全局的

var msg = "Thanks for visiting";


window.onunload = function () {

console.log(msg);

};

})();

上下文对象是通过this变量体现的,这个变量永远指向当前代码所处的对象中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

var obj = {

yes : function(){

//this == obj

this.val = true;

},

no : function(){

this.val = false;

}

};

console.log(obj.val == null);//true

//执行了yes函数后,将val属性与'obj'对象关联起来

obj.yes();

console.log(obj.val == true);//true

String 原型方法的扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    //公共正则表达式处理函数

String.prototype.Regular = function(reg){

var result = true;

if(this.length > 0){

if(!reg.test(this)){

result = false;

}

}

return result;

}



//.trim()方法

String.prototype.trim = function () {

return this.replace(/(^\s*)|(\s*$)/g,'');

};

^表示字符串必须以后面的规则开头,而(^\s*) 表示的就是以0个空格或者多个空格开头,后面的(\s*$) 的意思就是, 以0个空格或者多个空格结尾。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  //判断输入内容是否为空

String.prototype.isNull = function(){

return this.trim().length == 0 ? true : false;

}


//判断输入的字符是否为英文字母\数字\下划线

String.prototype.isVersion = function(){

var reg = /^([a-zA-Z_])([a-zA-Z0-9_.])*$/;

return this.Regular(reg);

}


// 判断输入的字符串,不包括单引号

String.prototype.isString = function(){

var reg = /^[^']*$/;

return this.Regular(reg);

}



//判断输入的字符是否为英文字母

String.prototype.isLetter = function(){

var reg = /^[a-zA-Z]+$/;

return this.Regular(reg);

}


constructor属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14

function User(){}

var me = new User();

console.log(me.constructor);//[Function: User]



//用前一个对象的Constructor引用来创建一个新的User对象

var you = new me.constructor();

console.log(me.constructor == you.constructor);//true

Object.create

1
2
3
4
5
6
7
8
9
function Parent(){}

var o = Object.create(Parent.prototype);

console.log(o instanceof Parent);//true

console.log(o instanceof Object);//true

console.log(Object.prototype.toString.call(o));//[object Object]


“数据属性”是可获取且可设置值的属性。 数据属性描述符包含 value 特性,以及 writable、enumerableconfigurable 特性。 如果未指定最后三个特性,则它们默认为 false

1
2
3
4
5
6
7
8

function Parent(){}

var o = Object.create(Parent);

console.log(o instanceof Parent);//false

console.log(o instanceof Object);//true

另外一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

var book1 = {

title:"JS高级程序设计",

pages : 1001,

getTitle:function(){

console.log(this.title);

}

};

var book2 = Object.create(book1,{

//title会成为所创建对象的数据属性

title:{

configurable:true,

enumerable:true,

value:"JS权威指南",

wratable:true

}

});

book1.getTitle(); //"JS高级程序设计"

book2.getTitle(); //"JS权威指南"



console.log(book1.hasOwnProperty("getTitle")); //true

console.log('pages' in book2); //true

console.log(book2.hasOwnProperty("getTitle")); //false

console.log(book1.isPrototypeOf(book2));//true


再看另一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

function Constructor(){}

obj = new Constructor();

// 上面的一句就相当于:

obj = Object.create(Constructor.prototype);

console.log(obj);//{}

console.log(Object.create(Constructor.prototype));//{}



console.log(obj instanceof Constructor);//true

console.log(Constructor.prototype.isPrototypeOf(obj));//true



var foo;

foo = {};

// 以字面量方式创建的空对象就相当于:

foo = Object.create(Object.prototype);

另外:

1
2
3
console.log(Object.prototype);//{}

console.log(Object.create(Object.prototype));//{}

通过Object.create(Object.prototype) 创建的实例对象就继承了Object原型下的属性和方法。

javascript所有function类型的对象都有一个prototype属性。这个prototype属性本身又是一个object类型的对象,原型对象都包含一个指向构造函数的指针,而每一个实例也都包含一个指向原型对象内部的指针。

参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create

prototype原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

function User(){}

var u1 = new User();

console.log(u1.prototype);//使用对象实例无法访问到prototype

console.log(User.prototype);//{},使用构造函数名访问prototype

console.log(u1.__proto__);//{},使用对象实例访问prototype的指针



//使用字面量的方式创建原型对象,这里{}就是对象

User.prototype = {

name : "trigkit4",

age : 22

};

使用构造函数创建原型对象和使用字面量创建对象在使用上基本相同,但还是有些区别,字面量创建的方式使用constructor属性不会指向实例,而会指向Object,构造函数创建的方式则相反

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

function User(){}

User.prototype = {

name : "trigkit4",

age : 22

};

var u1 = new User();

console.log(u1.constructor);//function Object() {[native code]}

console.log(u1 instanceof User);//true

console.log(u1.constructor == User);//false

console.log(u1.constructor == Object);//true



//如果想让字面量方式的constructor指向实例对象,可以这么做:

User.prototype = {

constructor : User;

}

字面量方式为什么constructor会指向Object?因为User.prototype = {};这种写法其实就是创建了一个新对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

function User(){

User.prototype = {

constructor : User

};

var u1 = new User();

console.log(User.constructor);//[Function: Function]

console.log(u1.constructor == User);//true


另一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

(function () {



console.log(Object.prototype);//{}

console.log(Array.prototype);//[]

console.log(Array.prototype.push);//[Function: push]

console.log(Function.prototype);//[Function: Empty]

console.log(Function.prototype.bind);//[Function: bind]


})();

Object.prototype.toString

toString()方法被调用时,会执行下面的操作步骤:

1
2
3
4
5
6
7
8
9
10

如果this的值为undefined,则返回"[object Undefined]".

如果this的值为null,则返回"[object Null]".

让O成为调用ToObject(this)的结果.

让class成为O的内部属性[[Class]]的值.

返回三个字符串"[object ", class, 以及 "]"连接后的新字符串.

由于 JavaScript 中一切都是对象,任何都不例外,对所有值类型应用Object.prototype.toString.call()

方法结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

console.log(Object.prototype.toString.call(123)) //[object Number]

console.log(Object.prototype.toString.call('123')) //[object String]

console.log(Object.prototype.toString.call(undefined)) //[object Undefined]

console.log(Object.prototype.toString.call(true)) //[object Boolean]

console.log(Object.prototype.toString.call({})) //[object Object]

console.log(Object.prototype.toString.call([])) //[object Array]

console.log(Object.prototype.toString.call(function(){})) //[object Function]


所有类型都会得到不同的字符串,几乎完美。

JavaScript中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过Object.prototype.toString方法.

面向对象

下面是来自Prototype.js的一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

//创建一个名为"Class"的全局对象

var Class = {

//它只有一个函数,其作用是创建一个新的对象构造函数

create: function(){

//创建一个匿名的对象构造函数

return function () {

//调用它本身的初始化方法

this.initialize.apply(this,arguments);

}

}

};



//给Object对象添加一个新的静态方法,它的作用是把属性从一个对象复制到另一个中

Object.extend = function (destination,source) {

//遍历所有要扩展的属性

for(property in source){

//然后将他们添加到目标对象中

destination[property] = source[property];

}

};

成员操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

function aFunc(){}//或者var aFunc = function(){};



aFunc.oProperty = "函数的一个属性";

aFunc.aMethod = function(){

console.log("函数的一个方法");

};


console.log(aFunc["oProperty"]);//将函数当成数组以属性名作为下标来访问属性

console.log(aFunc["aMethod"]());//将函数当数组以方法名作为下标来调用方法


//遍历函数的所有属性和方法

for(var s in aFunc){

console.log(s + "is a "+typeof(aFunc[s]));

}

特权方法与私有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
function Constructor(msg){

this.Message = msg;



//私有属性

var separator = '-';

var owner = this;



//私有方法

function alertMessage(){

console.log(owner.Message);

}


alertMessage();




//特权方法(也是公有方法)

this.aptMessage = function (str) {

this.Message += separator + str;

alertMessage();

}


}



//公有方法

Constructor.prototype.clearMessage = function (str) {

this.Message = '';

};




//静态属性

Constructor.name = 'trigkit4';



//静态方法

Constructor.alertName = function (name) {

console.log(this.name);

};


特权方法是指在构造函数的作用域中使用this关键字定义的方法;与私有方法不同,特权方法能够被公开访问,而且还能够访问私有成员。

由于私有和特权成员在函数的内部,因此它们会被带到函数的每个实例中。

公有的原型成员是对象蓝图的一部分,适用于通过new关键字实例化的该对象的每个实例 静态成员只适用于对象的一个特殊实例

使用对象字面量语法来向prototype属性添加所有公有成员:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Constructor(){

//私有和特权成员

}



//公有方法

Constructor.prototype = {

propertyA: 'value1',

propertyB: 'value2',

methodA: function(){},

methodB: function(){}

};


删除不要的节点

DOM 元素在浏览器中所占用的空间是非常大的,要及时回收不用的节点:

1
2
3
4
5
6

var node = parentNode.removeChild(node);

node = null;//设置为空,释放空间

CollectGarbage();//IE,回收资源